
#include "../Main.h"

#include "FridgeScript_API.h"

#include "FridgeScript.h"
#include "AudioHelper.h"
#include "../Preset/Element.h"
#include "../Visuals/Visuals_Main.h"
#include "../Visuals/RenderTarget.h"
#include "../Widgets/Widget.h"
#include "../Widgets/TextEditor.h"
#include "../Controller/Controller_Screen_Editor.h"

static bool CheckRendering()
{
	if( !Visuals_Main::IsRenderingPreset() )
	{
		FridgeScript::AppendToScriptOutput( "! Calling render function outside of Render()\r\n" );
		return false;
	}
	return true;
}

static bool CheckIniting()
{
	const Element* pxActiveElement = Element::GetActiveElement();
	
	if( pxActiveElement && !pxActiveElement->IsIniting() )
	{
		FridgeScript::AppendToScriptOutput( "! Calling init function outside of Init()\r\n" );
		return false;
	}
	return true;
}

static bool g_bNeedGLEnd = false;

static float g_fRed = 1.0f;
static float g_fGreen = 1.0f;
static float g_fBlue = 1.0f;
static float g_fAlpha = 1.0f;

class TextEditor;

// -------------------- API Wrapper functions --------------------------------

// Rather annoyingly we need C linkage for lua

extern "C"
{
	int DebugPrint( lua_State* pxLua )
	{
		// == DebugPrint
		// In - Any stringable type
		// Op - Print to the script output

		if( lua_isstring( pxLua, -1 ) )
		{
			const char* szString = lua_tostring( pxLua, -1 );
			if( szString )
			{
				FridgeScript::AppendToScriptOutput( szString );
				FridgeScript::AppendToScriptOutput( "\r\n" );
			}
		}
		return 0;
	}

	int ClearScreen( lua_State* pxLua )
	{
		// == ClearScreen
		// In - Nothing
		// Op - Clear the back buffer
		
		if( !CheckRendering() )
		{
			return 0;
		}

		// TODO: preserve state
		glColor4f( g_fRed, g_fGreen, g_fBlue, g_fAlpha );
		glBindTexture( GL_TEXTURE_2D, 0 );
		glBegin( GL_QUADS );
	
			glVertex2i( -1, -1 );
			glVertex2i( -1, 1 );
			glVertex2i( 1, 1 );
			glVertex2i( 1, -1 );
	
		glEnd();

		return 0;
	}

	int StartPrim( lua_State* pxLua )
	{
		// == StartPrim
		// In - Primitive type:	"lines"
		//						"quads"
		//						"triangles"
		// Op - Start drawing primitives (glBegin wrapper)

		if( !CheckRendering() )
		{
			return 0;
		}

		if( g_bNeedGLEnd )
		{
			FridgeScript::AppendToScriptOutput( "! Missing a call to EndPrim\r\n" );
			return 0;
		}

		if( lua_isstring( pxLua, -1 ) )
		{
			const char* szString = lua_tostring( pxLua, -1 );
			if( szString && szString[0] )
			{
				const size_t xLen = lua_strlen( pxLua, -1 );

				g_bNeedGLEnd = true;

				if( strncmp( szString, "lines", xLen ) == 0 )
				{
					glBegin( GL_LINES );
				}
				else if( strncmp( szString, "linestrip", xLen ) == 0 )
				{
					glBegin( GL_LINE_STRIP );
				}
				else if( strncmp( szString, "quads", xLen ) == 0 )
				{
					glBegin( GL_QUADS );
				}
				else if( strncmp( szString, "triangles", xLen ) == 0 )
				{
					glBegin( GL_TRIANGLES );
				}
				else
				{
					FridgeScript::AppendToScriptOutput( "! Unknown primitive type: " );
					FridgeScript::AppendToScriptOutput( szString );
					FridgeScript::AppendToScriptOutput( "\r\n" );
					g_bNeedGLEnd = false;
				}
			}
		}
		return 0;
	}

	int EndPrim( lua_State* pxLua )
	{
		// == EndPrim
		// In - Nothing
		// Op - Finish drawing primitives (glEnd wrapper)

		if( !CheckRendering() )
		{
			return 0;
		}

		if( !g_bNeedGLEnd )
		{
			FridgeScript::AppendToScriptOutput( "! Unexpected EndPrims\r\n" );
			return 0;
		}

		glEnd();
		g_bNeedGLEnd = false;

		return 0;
	}

	int Vert2( lua_State* pxLua )
	{
		// == Vert2
		// In - x,y
		// Op - Add a 2D primitive vertex

		if( !CheckRendering() )
		{
			return 0;
		}

		if( !g_bNeedGLEnd )
		{
			FridgeScript::AppendToScriptOutput( "! Expected StartPrims\r\n" );
			return 0;
		}
		
		lua_Number xNumY = lua_tonumber( pxLua, -1 );
		lua_Number xNumX = lua_tonumber( pxLua, -2 );

		glColor4f( g_fRed, g_fGreen, g_fBlue, g_fAlpha );
		glVertex2f( (GLfloat)xNumX, (GLfloat)xNumY );

		return 0;
	}

	int Osc( lua_State* pxLua )
	{
		// == Osc
		// In - Time  (0..1) - position in osc data
		//      Width (0..1) - peak over this range
		//      Chan  ("left", "right", "mono") - channel
		// Op - Return oscilloscope data

		FRIDGE_AUDIO_CHANNEL eChan = CHANNEL_MONO;

		const char* szChan = 0;
		if( lua_isstring( pxLua, -1 ) )
		{
			szChan = lua_tostring( pxLua, -1 );
			if( szChan && szChan[0] )
			{
				const size_t xLen = lua_strlen( pxLua, -1 );

				if( strncmp( szChan, "left", xLen ) == 0 )
				{
					eChan = CHANNEL_LEFT;
				}
				else if( strncmp( szChan, "right", xLen ) == 0 )
				{
					eChan = CHANNEL_RIGHT;
				}
				else if( strncmp( szChan, "mono", xLen ) != 0 )
				{
					FridgeScript::AppendToScriptOutput( "! Unknown osc channel: " );
					FridgeScript::AppendToScriptOutput( szChan );
					FridgeScript::AppendToScriptOutput( "\r\n" );
				}
			}
		}
		lua_Number xNumWidth = lua_tonumber( pxLua, -2 );
		lua_Number xNumTime = lua_tonumber( pxLua, -3 );

		const float fTime = MACRO_CLAMP( (float)xNumTime, 0.0f , 1.0f );
		const float fWidth = MACRO_CLAMP( (float)xNumWidth, 0.0f , 1.0f );

		const float fOsc = AudioHelper::GetOsc( fTime, fWidth, eChan );

		lua_pushnumber( pxLua, fOsc );
		return 1;
	}

	int SetColour( lua_State* pxLua )
	{
		// == Color/Colour
		// In - Red   (0..1)
		//      Green (0..1)
		//      Blue  (0..1)
		//      Alpha (0..1)
		// Op - Set the global colour state

		lua_Number xAlpha = lua_tonumber( pxLua, -1 );
		lua_Number xBlue = lua_tonumber( pxLua, -2 );
		lua_Number xGreen = lua_tonumber( pxLua, -3 );
		lua_Number xRed = lua_tonumber( pxLua, -4 );

		g_fRed = MACRO_CLAMP( (float)xRed, 0.0f, 1.0f );
		g_fGreen = MACRO_CLAMP( (float)xGreen, 0.0f, 1.0f );
		g_fBlue = MACRO_CLAMP( (float)xBlue, 0.0f, 1.0f );
		g_fAlpha = MACRO_CLAMP( (float)xAlpha, 0.0f, 1.0f );

		return 0;
	}
	
	int SetColourHex( lua_State* pxLua )
	{
		// == Color/Colour
		// In - Color hex as u_int
		// Op - Set the global colour state
		
		u_int uColourHex = (u_int)lua_tonumber( pxLua, -1 );
		
		g_fRed		= MACRO_CLAMP( (float)( ( ( uColourHex ) >> 24 ) & 0xFF ) / 255.0f, 0.0f, 1.0f );
		g_fGreen	= MACRO_CLAMP( (float)( ( ( uColourHex ) >> 16 ) & 0xFF ) / 255.0f, 0.0f, 1.0f );
		g_fBlue		= MACRO_CLAMP( (float)( ( ( uColourHex ) >> 8  ) & 0xFF ) / 255.0f, 0.0f, 1.0f );
		g_fAlpha	= MACRO_CLAMP( (float)( ( ( uColourHex ) )		 & 0xFF ) / 255.0f, 0.0f, 1.0f );
		
		return 0;
	}

	int UV( lua_State* pxLua )
	{
		// == UV
		// In - U (0..1)
		//      V (0..1)
		// Op - Set the current texture coords state

		if( !CheckRendering() )
		{
			return 0;
		}

		lua_Number xV = lua_tonumber( pxLua, -1 );
		lua_Number xU = lua_tonumber( pxLua, -2 );

		glTexCoord2f( (float)xU, (float)xV );

		return 0;
	}

	int BindTex( lua_State* pxLua )
	{
		// == BindTex
		// In - Texture (string)
		//      ["framebuffer"] - Bind the current frame buffer
		// Op - Bind the current texture

		if( !CheckRendering() )
		{
			return 0;
		}

		const char* szChan = 0;
		if( lua_isstring( pxLua, -1 ) )
		{
			szChan = lua_tostring( pxLua, -1 );
			if( szChan && szChan[0] )
			{
				const size_t xLen = lua_strlen( pxLua, -1 );
				if( strncmp( szChan, "framebuffer", xLen ) == 0 )
				{
					Visuals_Main::ResolveRT( true );

					Visuals_Main::BindRTAsTexture();
				}
				if( strncmp( szChan, "group", xLen ) == 0 )
				{
					const Element* pxActiveElement = Element::GetActiveElement();

					if( pxActiveElement->IsGroup() )
					{
						Visuals_Main::ResolvePreviousRT();
						Visuals_Main::BindRTAsTexture();
					}
					else
					{
						FridgeScript::AppendToScriptOutput( "! Not a group\r\n" );
					}
				}
				else if( strncmp( szChan, "none", xLen ) == 0 )
				{
					glBindTexture( GL_TEXTURE_2D, 0 );
				}
				else
				{
					FridgeScript::AppendToScriptOutput( "! Unknown texture: " );
					FridgeScript::AppendToScriptOutput( szChan );
					FridgeScript::AppendToScriptOutput( "\r\n" );
				}
			}
		}
		return 0;
	}
	
	int Control( lua_State *pxLua )
	{
		// == Controller
		// In - Control Name (string)
		// Op - Returns value from named controller
		
		const Element* pxActiveElement = Element::GetActiveElement();

		const char* szName = 0;
		
		if ( lua_isstring( pxLua, -1 ) )
		{
			szName = lua_tostring( pxLua, -1 );
			if ( szName && szName[ 0 ] )
			{
				const ExposedVariable* pxExposedVariable = pxActiveElement->GetExposedVariable( szName );
				
				if ( pxExposedVariable )
				{
					const double dValue = pxExposedVariable->GetValue();
					
					lua_pushnumber( pxLua, dValue );
				}
				else 
				{
					FridgeScript::AppendToScriptOutput( "! Unknown control name: " );
					FridgeScript::AppendToScriptOutput( szName );
					FridgeScript::AppendToScriptOutput( "\r\n" );
				}
			}
		}
		return 1;
	}
}

// --------------------------------------------------------------------------

extern "C" typedef int FS_APIFunc( lua_State* );

struct FS_API_ListEntry
{
	FS_APIFunc* pfnFunc;
	char* szIdentifier;
};

static const FS_API_ListEntry g_axFunctionList[] = 
{ 
	{ &DebugPrint, "DebugPrint" },
	{ &ClearScreen, "ClearScreen" },
	{ &StartPrim, "StartPrims" },
	{ &EndPrim, "EndPrims" },
	{ &Vert2, "Vert2" },
	{ &UV, "UV" },
	{ &Osc, "Osc" },
	{ &SetColour, "Colour" },
	{ &SetColour, "Color" },
	{ &SetColourHex, "ColourHex" },
	{ &SetColourHex, "ColorHex" },
	{ &BindTex, "BindTex" },
	{ &Control, "Control" }
};

void FridgeScript_API::RegisterFunctions( lua_State* pxLua )
{
	u_int uNumFuncs = sizeof( g_axFunctionList ) / sizeof( g_axFunctionList[0] );

	for( u_int uFunc = 0; uFunc < uNumFuncs; ++uFunc )
	{
		lua_register( pxLua, g_axFunctionList[uFunc].szIdentifier, g_axFunctionList[uFunc].pfnFunc );
	}
}

void FridgeScript_API::CleanUp()
{
	if( g_bNeedGLEnd )
	{
		FridgeScript::AppendToScriptOutput( "! Expected EndPrims\r\n" );
		glEnd();
		g_bNeedGLEnd = false;
	}
}

// eof
